home *** CD-ROM | disk | FTP | other *** search
/ Aminet 24 / Aminet 24 (1998)(GTI - Schatztruhe)[!][Apr 1998].iso / Aminet / comm / mail / Mutt089src.lha / Mutt-0.89i-AMIGA / src / rfc2047.c < prev    next >
C/C++ Source or Header  |  1998-01-28  |  7KB  |  360 lines

  1. /*
  2.  * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
  3.  * 
  4.  *     This program is free software; you can redistribute it and/or modify
  5.  *     it under the terms of the GNU General Public License as published by
  6.  *     the Free Software Foundation; either version 2 of the License, or
  7.  *     (at your option) any later version.
  8.  * 
  9.  *     This program is distributed in the hope that it will be useful,
  10.  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  *     GNU General Public License for more details.
  13.  * 
  14.  *     You should have received a copy of the GNU General Public License
  15.  *     along with this program; if not, write to the Free Software
  16.  *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  */ 
  18.  
  19. #include "mutt.h"
  20. #include "mime.h"
  21. #include "rfc2047.h"
  22.  
  23. #include <ctype.h>
  24. #include <string.h>
  25.  
  26. typedef void encode_t (char *, unsigned char *, size_t);
  27.  
  28. extern char *tspecials;
  29. extern char B64Chars[];
  30.  
  31. static void q_encode_string (char *d, unsigned char *s, size_t len)
  32. {
  33.   char charset[SHORT_STRING];
  34.   int cslen;
  35.   int wordlen;
  36.   char *wptr = d;
  37.  
  38.   snprintf (charset, sizeof (charset), "=?%s?Q?",
  39.         strcasecmp ("us-ascii", charset) == 0 ? "unknown-8bit" : Charset);
  40.   cslen = strlen (charset);
  41.  
  42.   /*
  43.    * Hack to pull the Re: and Fwd: out of the encoded word for better
  44.    * handling by agents which do not support RFC2047.
  45.    */
  46.  
  47.   if (strncasecmp ("re: ", (char *) s, 4) == 0)
  48.   {
  49.     strncpy (wptr, (char *) s, 4);
  50.     wptr += 4;
  51.     s += 4;
  52.   }
  53.   else if (strncasecmp ("fwd: ", (char *) s, 5) == 0)
  54.   {
  55.     strncpy (wptr, (char *) s, 5);
  56.     wptr += 5;
  57.     s += 5;
  58.   }
  59.  
  60.   strcpy (wptr, charset);
  61.   wptr += cslen;
  62.   wordlen = cslen;
  63.  
  64.   while (*s)
  65.   {
  66.     if (wordlen >= 72)
  67.     {
  68.       strcpy(wptr, "?=\n ");
  69.       wptr += 4;
  70.       strcpy(wptr, charset);
  71.       wptr += cslen;
  72.       wordlen = cslen;
  73.     }
  74.  
  75.     if (*s == ' ')
  76.     {
  77.       *wptr++ = '_';
  78.       wordlen++;
  79.     }
  80.     else if (*s & 0x80 || (strchr (tspecials, *s) != NULL))
  81.     {
  82.       if (wordlen >= 70)
  83.       {
  84.     strcpy (wptr, "?=\n ");
  85.     wptr += 4;
  86.     strcpy (wptr, charset);
  87.     wptr += cslen;
  88.     wordlen = cslen;
  89.       }
  90.       sprintf (wptr, "=%2X", *s);
  91.       wptr += 3;
  92.       wordlen += 3;
  93.     }
  94.     else
  95.     {
  96.       *wptr++ = *s;
  97.       wordlen++;
  98.     }
  99.     s++;
  100.   }
  101.   strcpy (wptr, "?=");
  102. }
  103.  
  104. static void b_encode_string (char *d, unsigned char *s, size_t len)
  105. {
  106.   char charset[SHORT_STRING];
  107.   char *wptr = d;
  108.   int cslen;
  109.   int wordlen;
  110.  
  111.   snprintf (charset, sizeof (charset), "=?%s?B?", Charset);
  112.   cslen = strlen (charset);
  113.   strcpy (wptr, charset);
  114.   wptr += cslen;
  115.   wordlen = cslen;
  116.  
  117.   while (*s)
  118.   {
  119.     if (wordlen >= 71)
  120.     {
  121.       strcpy(wptr, "?=\n ");
  122.       wptr += 4;
  123.       strcpy(wptr, charset);
  124.       wptr += cslen;
  125.       wordlen = cslen;
  126.     }
  127.  
  128.     *wptr++ = B64Chars[ (*s >> 2) & 0x3f ];
  129.     *wptr++ = B64Chars[ ((*s & 0x3) << 4) | ((*(s+1) >> 4) & 0xf) ];
  130.     s++;
  131.     if (*s)
  132.     {
  133.       *wptr++ = B64Chars[ ((*s & 0xf) << 2) | ((*(s+1) >> 6) & 0x3) ];
  134.       s++;
  135.       if (*s)
  136.       {
  137.     *wptr++ = B64Chars[ *s & 0x3f ];
  138.     s++;
  139.       }
  140.       else
  141.     *wptr++ = '=';
  142.     }
  143.     else
  144.     {
  145.       *wptr++ = '=';
  146.       *wptr++ = '=';
  147.     }
  148.  
  149.     wordlen += 4;
  150.   }
  151.  
  152.   strcpy(wptr, "?=");
  153. }
  154.  
  155. void rfc2047_encode_string (char *d, unsigned char *s, size_t l)
  156. {
  157.   int count = 0;
  158.   int len;
  159.   unsigned char *p = s;
  160.   encode_t *encoder;
  161.  
  162.   /* First check to see if there are any 8-bit characters */
  163.   while (*p)
  164.   {
  165.     if (*p & 0x80) count++;
  166.     p++;
  167.   }
  168.   if (!count)
  169.   {
  170.     strfcpy (d, (char *)s, l);
  171.     return;
  172.   }
  173.  
  174.   if (strcasecmp("us-ascii", Charset) == 0 ||
  175.       strncasecmp("iso-8859", Charset, 8) == 0)
  176.     encoder = q_encode_string;
  177.   else
  178.   {
  179.     /* figure out which encoding generates the most compact representation */
  180.     len = strlen ((char *) s);
  181.     if ((count * 2) + len <= (4 * len) / 3)
  182.       encoder = q_encode_string;
  183.     else
  184.       encoder = b_encode_string;
  185.   }
  186.  
  187.   (*encoder)(d, s, l);
  188. }
  189.  
  190. void rfc2047_encode_adrlist (ADDRESS *addr)
  191. {
  192.   ADDRESS *ptr = addr;
  193.   char buffer[STRING];
  194.  
  195.   while (ptr)
  196.   {
  197.     if (ptr->personal)
  198.     {
  199.       rfc2047_encode_string (buffer, (unsigned char *)ptr->personal, sizeof (buffer));
  200.       safe_free ((void **)&ptr->personal);
  201.       ptr->personal = safe_strdup (buffer);
  202.     }
  203.     ptr = ptr->next;
  204.   }
  205. }
  206.  
  207. static int rfc2047_decode_word (char *d, const char *s, size_t len)
  208. {
  209.   char *p = safe_strdup (s);
  210.   char *pp = p;
  211.   char *pd = d;
  212.   int enc = 0, filter = 0, count = 0, c1, c2, c3, c4;
  213.  
  214.   while ((pp = strtok (pp, "?")) != NULL)
  215.   {
  216.     count++;
  217.     switch (count)
  218.     {
  219.       case 2:
  220.     if (strcasecmp (pp, Charset) != 0)
  221.       filter = 1;
  222.     break;
  223.       case 3:
  224.     if (toupper (*pp) == 'Q')
  225.       enc = ENCQUOTEDPRINTABLE;
  226.     else if (toupper (*pp) == 'B')
  227.       enc = ENCBASE64;
  228.     else
  229.       return (-1);
  230.     break;
  231.       case 4:
  232.     if (enc == ENCQUOTEDPRINTABLE)
  233.     {
  234.       while (*pp && len > 0)
  235.       {
  236.         if (*pp == '_')
  237.         {
  238.           *pd++ = ' ';
  239.           len--;
  240.         }
  241.         else if (*pp == '=')
  242.         {
  243.           *pd++ = (hexval(pp[1]) << 4) | hexval(pp[2]);
  244.           len--;
  245.           pp += 2;
  246.         }
  247.         else
  248.         {
  249.           *pd++ = *pp;
  250.           len--;
  251.         }
  252.         pp++;
  253.       }
  254.       *pd = 0;
  255.     }
  256.     else if (enc == ENCBASE64)
  257.     {
  258.       while (*pp && len > 0)
  259.       {
  260.         c1 = Index_64[(int) pp[0]];
  261.         c2 = Index_64[(int) pp[1]];
  262.         *pd++ = (c1 << 2) | ((c2 >> 4) & 0x3);
  263.         if (--len == 0) break;
  264.         
  265.         if (pp[2] == '=') break;
  266.  
  267.         c3 = Index_64[(int) pp[2]];
  268.         *pd++ = ((c2 & 0xf) << 4) | ((c3 >> 2) & 0xf);
  269.         if (--len == 0)
  270.           break;
  271.  
  272.         if (pp[3] == '=')
  273.           break;
  274.  
  275.         c4 = Index_64[(int) pp[3]];   
  276.         *pd++ = ((c3 & 0x3) << 6) | c4;
  277.         if (--len == 0)
  278.           break;
  279.  
  280.         pp += 4;
  281.       }
  282.       *pd = 0;
  283.     }
  284.     break;
  285.     }
  286.     pp = 0;
  287.   }
  288.   safe_free ((void **) &p);
  289.   if (filter)
  290.   {
  291.     pd = d;
  292.     while (*pd)
  293.     {
  294.       if (!IsPrint ((unsigned char) *pd))
  295.     *pd = '?';
  296.       pd++;
  297.     }
  298.   }
  299.   return (0);
  300. }
  301.  
  302. /* try to decode anything that looks like a valid RFC2047 encoded
  303.  * header field, ignoring RFC822 parsing rules
  304.  */
  305. void rfc2047_decode (char *d, const char *s, size_t dlen)
  306. {
  307.   const char *p, *q;
  308.   size_t n;
  309.   int found_encoded = 0;
  310.  
  311.   dlen--; /* save room for the terminal nul */
  312.  
  313.   while (*s && dlen > 0)
  314.   {
  315.     if ((p = strstr (s, "=?")) == NULL ||
  316.     (q = strchr (p + 2, '?')) == NULL ||
  317.     (q = strchr (q + 1, '?')) == NULL ||
  318.     (q = strstr (q + 1, "?=")) == NULL)
  319.     {
  320.       /* no encoded words */
  321.       if (d != s)
  322.     strfcpy (d, s, dlen + 1);
  323.       return;
  324.     }
  325.  
  326.     if (p != s)
  327.     {
  328.       n = (size_t) (p - s);
  329.       /* ignore spaces between encoded words */
  330.       if (!found_encoded || strspn (s, " \t") != n)
  331.       {
  332.     if (n > dlen)
  333.       n = dlen;
  334.     if (d != s)
  335.       memcpy (d, s, n);
  336.     d += n;
  337.     dlen -= n;
  338.       }
  339.     }
  340.  
  341.     rfc2047_decode_word (d, p, dlen);
  342.     found_encoded = 1;
  343.     s = q + 2;
  344.     n = strlen (d);
  345.     dlen -= n;
  346.     d += n;
  347.   }
  348.   *d = 0;
  349. }
  350.  
  351. void rfc2047_decode_adrlist (ADDRESS *a)
  352. {
  353.   while (a)
  354.   {
  355.     if (a->personal && strstr (a->personal, "=?") != NULL)
  356.       rfc2047_decode (a->personal, a->personal, strlen (a->personal) + 1);
  357.     a = a->next;
  358.   }
  359. }
  360.